use super::{json, Client, MapSv, RNull, Resp, SStr, SvCache, VBase, Value, VecS, VecSv, VecV};

pub trait Content {
    fn item_content(&self, cache: &SvCache, client: &Client, sn: &str, buffer: &mut bool) -> Resp {
        *buffer = true;

        match cache.get(sn) {
            Some(v) => Ok(v),
            None => {
                *buffer = false;
                client.get(sn)
            }
        }
    }

    fn dir_content(
        &self,
        cache: &SvCache,
        client: &Client,
        sn: &str,
        buf: &mut bool,
    ) -> Result<VecS, Value> {
        let body = self.item_content(cache, client, sn, buf)?;
        let body = body.ckarray()?;

        let mut ret = Vec::new();

        for d in body.iter() {
            let d = d.ckstr()?;
            ret.push(d.to_string());
        }

        Ok(ret)
    }

    fn rm_key(
        &self,
        cache: &SvCache,
        client: &Client,
        sn: &str,
        val: &VecV,
        put: &mut VecSv,
    ) -> RNull {
        let mut item = MapSv::new();
        let mut is_buffer = false;

        if let Ok(body) = self.item_content(cache, client, sn, &mut is_buffer) {
            item = body.ckobj()?.clone();
        }

        for k in val.iter() {
            let k = k.ckstr()?;

            if k == "sn" {
                continue;
            }

            item.remove(k);
        }

        put.push((sn.to_string(), json!(item)));

        Ok(())
    }

    fn merge_item(
        &self,
        cache: &SvCache,
        client: &Client,
        sn: &str,
        val: &MapSv,
        put: &mut VecSv,
    ) -> RNull {
        let mut item = MapSv::new();
        let mut is_buffer = false;

        if let Ok(body) = self.item_content(cache, client, sn, &mut is_buffer) {
            item = body.ckobj()?.clone();
        }

        for (k, v) in val.iter() {
            item.insert(k.to_string(), v.clone());
        }

        put.push((sn.to_string(), json!(item)));

        Ok(())
    }

    fn dir_del(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        sn: &str,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> bool {
        let mut buf = false;
        if let Ok(mut body) = self.dir_content(cache, client, dsn, &mut buf) {
            let sn = sn.to_string();

            if let Ok(p) = body.binary_search(&sn) {
                body.remove(p);

                if body.is_empty() {
                    del.push(dsn.to_string());
                    return true;
                }

                put.push((dsn.to_string(), json!(body)));
            }
        }

        false
    }

    fn run_del(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        sn: &str,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> bool {
        let mut buf = false;
        if let Ok(mut body) = self.dir_content(cache, client, dsn, &mut buf) {
            let sn = sn.to_string();

            if let Ok(p) = body.binary_search_by_key(&sn, |x| x[20..].to_string()) {
                body.remove(p);

                if body.is_empty() {
                    del.push(dsn.to_string());
                    return true;
                }

                put.push((dsn.to_string(), json!(body)));
            }
        }

        false
    }

    fn dir_del_set(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        set: &SStr,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> bool {
        let mut buf = false;
        if let Ok(body) = self.dir_content(cache, client, dsn, &mut buf) {
            let mut md = Vec::new();

            for d in body.iter() {
                if set.contains(d) {
                    continue;
                }

                md.push(d);
            }

            if md.is_empty() {
                del.push(dsn.to_string());
                return true;
            }

            if md.len() != body.len() {
                put.push((dsn.to_string(), json!(md)));
            }
        }

        false
    }

    fn run_del_set(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        set: &SStr,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> bool {
        let mut buf = false;
        if let Ok(body) = self.dir_content(cache, client, dsn, &mut buf) {
            let mut md = Vec::new();

            for d in body.iter() {
                if set.contains(&d[20..]) {
                    continue;
                }

                md.push(d);
            }

            if md.is_empty() {
                del.push(dsn.to_string());
                return true;
            }

            put.push((dsn.to_string(), json!(md)));
        }

        false
    }

    fn dir_add(&self, cache: &SvCache, client: &Client, dsn: &str, sn: &str, put: &mut VecSv) {
        let mut cont = Vec::new();
        let mut buf = false;
        let sn = sn.to_string();

        if let Ok(mut body) = self.dir_content(cache, client, dsn, &mut buf) {
            if let Ok(_) = body.binary_search(&sn) {
                return;
            }

            cont.append(&mut body);
        }

        cont.push(sn);

        cont.sort_unstable();
        cont.dedup();

        put.push((dsn.to_string(), json!(cont)));
    }

    fn dir_add_vec(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        val: &VecV,
        put: &mut VecSv,
    ) -> RNull {
        if val.is_empty() {
            return Ok(());
        }

        let mut cont = Vec::new();
        let mut buf = false;

        if let Ok(mut body) = self.dir_content(cache, client, dsn, &mut buf) {
            cont.append(&mut body);
        }

        for d in val.iter() {
            let d = d.ckstr()?;
            cont.push(d.to_string());
        }

        cont.sort_unstable();
        cont.dedup();

        put.push((dsn.to_string(), json!(cont)));

        Ok(())
    }

    fn dir_add_set(
        &self,
        cache: &SvCache,
        client: &Client,
        dsn: &str,
        val: &MapSv,
        put: &mut VecSv,
    ) -> RNull {
        if val.is_empty() {
            return Ok(());
        }

        let mut cont = Vec::new();

        for (k, v) in val.iter() {
            put.push((format!("{dsn}{k}"), v.clone()));
            cont.push(json!(k));
        }

        self.dir_add_vec(cache, client, dsn, &cont, put)
    }
}
